﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using SharpAnalyzer.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace SharpAnalyzer.Analyzers
{
    internal sealed class MemberClassCouplingAnalyzer : ClassCouplingAnalyzerBase
    {
        public MemberClassCouplingAnalyzer(SemanticModel semanticModel)
          : base(semanticModel)
        {
        }

        public IEnumerable<string> Calculate(Member memberNode)
        {
            SyntaxNode syntaxNode = memberNode.SyntaxNode;
            switch (memberNode.Kind)
            {
                case MemberKind.Method:
                    CalculateMethodClassCoupling((MethodDeclarationSyntax)syntaxNode);
                    break;
                case MemberKind.Constructor:
                case MemberKind.Destructor:
                    CalculateGenericMemberClassCoupling((MemberDeclarationSyntax)syntaxNode);
                    break;
                case MemberKind.GetProperty:
                    CalculatePropertyClassCoupling((PropertyDeclarationSyntax)syntaxNode, SyntaxKind.GetAccessorDeclaration);
                    break;
                case MemberKind.SetProperty:
                    CalculatePropertyClassCoupling((PropertyDeclarationSyntax)syntaxNode, SyntaxKind.SetAccessorDeclaration);
                    break;
                case MemberKind.AddEventHandler:
                    CalculateEventClassCoupling((EventDeclarationSyntax)syntaxNode, SyntaxKind.AddAccessorDeclaration);
                    break;
                case MemberKind.RemoveEventHandler:
                    CalculateEventClassCoupling((EventDeclarationSyntax)syntaxNode, SyntaxKind.RemoveAccessorDeclaration);
                    break;
            }
            return GetCollectedTypesNames();
        }

        public override void VisitParameter(ParameterSyntax node)
        {
            base.VisitParameter(node);
            TypeSyntax type = node.Type;
            if (type == null)
                return;
            FilterType(type);
        }

        public override void VisitIdentifierName(IdentifierNameSyntax node)
        {
            base.VisitIdentifierName(node);
            SymbolInfo symbolInfo = SemanticModel.GetSymbolInfo(node);
            if (symbolInfo.Symbol == null)
                return;
            ISymbol symbol = symbolInfo.Symbol;
            switch (symbol.Kind)
            {
                case SymbolKind.NamedType:
                    FilterTypeSymbol((ITypeSymbol)symbol);
                    break;
                case SymbolKind.Parameter:
                    FilterTypeSymbol(((IParameterSymbol)symbol).Type);
                    break;
                case SymbolKind.Method:
                    FilterTypeSymbol((ITypeSymbol)symbol.ContainingType);
                    break;
                case SymbolKind.Field:
                    FilterTypeSymbol(((IFieldSymbol)symbol).Type);
                    break;
                case SymbolKind.Property:
                    FilterTypeSymbol((ITypeSymbol)symbol.ContainingType);
                    break;
                case SymbolKind.Event:
                    FilterTypeSymbol((ITypeSymbol)symbol.ContainingType);
                    break;
                case SymbolKind.Local:
                    FilterTypeSymbol(((ILocalSymbol)symbol).Type);
                    break;
                case SymbolKind.DynamicType:
                    FilterTypeSymbol((ITypeSymbol)symbol.ContainingType);
                    break;
            }
        }

        private void CalculateMethodClassCoupling(MethodDeclarationSyntax syntax)
        {
            Visit(syntax);
            FilterType(syntax.ReturnType);
        }

        private void CalculateGenericMemberClassCoupling(MemberDeclarationSyntax syntax)
        {
            Visit(syntax);
        }

        private void CalculatePropertyClassCoupling(PropertyDeclarationSyntax syntax, SyntaxKind kind)
        {
            FilterType(syntax.Type);
            BlockSyntax accessor = GetAccessor(syntax.AccessorList, kind);
            if (accessor == null)
                return;
            Visit(accessor);
        }

        private void CalculateEventClassCoupling(EventDeclarationSyntax syntax, SyntaxKind kind)
        {
            FilterType(syntax.Type);
            BlockSyntax accessor = GetAccessor(syntax.AccessorList, kind);
            if (accessor == null)
                return;
            Visit(accessor);
        }

        private static BlockSyntax GetAccessor(AccessorListSyntax accessorList, SyntaxKind kind)
        {
            return accessorList.Accessors.SingleOrDefault(x => x.Kind() == kind).Body;
        }
    }
}
